'''
Google Wave Personal Agent

On Each Wave blip, we parse out facts of the form:  /type item attribute value_may_contain_spaces
Triples are commited to the data store, where they can be queried later.

If time permits: we can consider natural language deduction of facts.

Author: Ryan Delucchi
'''

from waveapi import events
from waveapi import model
from waveapi import robot

from google.appengine.ext import webapp
import wsgiref.handlers

import re
from models import Fact
from models import Topic

# Decode a fact from the provided string.
def FactArgDecoder(s):
    s = s[s.find(' ') + 1:]
    value_pos = s.find(' ', s.find(' ') + 1) + 1
    item_and_attr, value = s[:value_pos].split(), s[value_pos:]
    return item_and_attr[0], item_and_attr[1], value

# Decode all arguments
def SimpleArgDecoder(s):
    return tuple(s.split()[1:])

# On a submitted or updated blip, process the associated document
def OnBlipEvent(properties, context):
    blip = context.GetBlipById(properties['blipId'])
    document = blip.GetDocument()
    Topic(text=document.GetText()).put()        # Create new topic data for the blip
    
    for line in document.GetText().splitlines():
        for pattern in OPERATIONS:
            if pattern.match(line) != None:
                op, decoder = OPERATIONS[pattern]
                result = op(*decoder(line))
                if result != None:
                    blip.GetDocument().SetText(result)

# Set the fact as defined by the provided item, attribute and value.
# this may involve creating or updating a prexisting fact.
def SetFact(item, attribute, value):
    fact = Fact.gql('WHERE item = :item AND attribute = :attribute', item=item, attribute=attribute)
    
    if fact == None or fact.count() == 0:
        fact = Fact(item=item, attribute=attribute, value=value)
    else:
        fact = fact.get()
        fact.value = value
    fact.put()
    return None

# Remove a single fact
def RemoveFact(item, attribute):
    result = Fact.gql('WHERE item = :item AND attribute = :attribute', item=item, attribute=attribute)
    
    if result != None and result.count() != 0:
        result.get().delete()

# Lookup all facts with the provided item/attribute combination
def Lookup(item, attribute=None):
    if attribute != None:
        facts = Fact.gql('WHERE item = :item AND attribute = :attribute', item=item, attribute=attribute)
    else:
        facts = Fact.gql('WHERE item = :item', item=item)
    
    if facts != None and facts.count() != 0:
        result = ""
        on_first = True
        for fact in facts:
            if not on_first:
                result += '\n'
            else:
                on_first = False
            result += '%s: %s = %s' % (fact.item, fact.attribute, fact.value)
        return result
    return None

# Define all regex pattern -> operation/arg-decoder mappings
OPERATIONS = {re.compile('/(fact) ([^ ]+) ([^ ])+ .+'): (SetFact, FactArgDecoder),
              re.compile('/(lookup) ([^ ]+)( [^ ]+)?$'): (Lookup, SimpleArgDecoder),
              re.compile('/(remove) ([^ ]+) ([^ ])+'): (RemoveFact, SimpleArgDecoder)}

if __name__ == '__main__':
  ### Configure The Waveagent robot and run
  waveagent = robot.Robot('waveagent', image_url='http://waveagent.appspot.com/assets/icon.png',
        version='1', profile_url='http://waveagent.appspot.com/')

  waveagent.RegisterHandler(events.BLIP_SUBMITTED, OnBlipEvent)
  waveagent.RegisterHandler(events.DOCUMENT_CHANGED, OnBlipEvent)
  waveagent.Run(debug=True)